package top.shaozuo.simple.configuration.microprofile.config;

import static java.util.stream.StreamSupport.stream;

import java.util.LinkedHashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigValue;
import org.eclipse.microprofile.config.spi.ConfigSource;
import org.eclipse.microprofile.config.spi.Converter;

import top.shaozuo.simple.configuration.microprofile.config.converts.Converters;
import top.shaozuo.simple.configuration.microprofile.config.source.ConfigSources;

public class JavaConfig implements Config {

	private ConfigSources configSources;
	private Converters converters;

	public JavaConfig(ConfigSources configSources, Converters converters) {
		this.configSources = configSources;
		this.converters = converters;
	}

	@Override
	public <T> T getValue(String propertyName, Class<T> propertyType) {
		ConfigValue configValue = getConfigValue(propertyName);
		if (configValue == null) {
			return null;
		}
		Optional<Converter<T>> convertHolder = getConverter(propertyType);
		if (convertHolder.isPresent()) {
			Converter<T> converter = convertHolder.get();
			return converter.convert(configValue.getValue());
		}
		return null;
	}

	@Override
	public ConfigValue getConfigValue(String propertyName) {
		String propertyValue = null;
		ConfigSource foundConfigSource = null;
		for (ConfigSource configSource : configSources) {
			propertyValue = configSource.getValue(propertyName);
			if (propertyValue != null) {
				foundConfigSource = configSource;
				break;
			}
		}
		if (propertyValue == null) {
			return null;
		}
		return new DefaultConfigValue(propertyName, propertyValue, propertyValue, foundConfigSource.getName(),
				foundConfigSource.getOrdinal());
	}

	@Override
	public <T> Optional<T> getOptionalValue(String propertyName, Class<T> propertyType) {
		T value = getValue(propertyName, propertyType);
		return Optional.ofNullable(value);
	}

	@Override
	public Iterable<String> getPropertyNames() {
		return stream(configSources.spliterator(), false).map(ConfigSource::getPropertyNames)
				.collect(LinkedHashSet::new, Set::addAll, Set::addAll);
	}

	@Override
	public Iterable<ConfigSource> getConfigSources() {
		return configSources;
	}

	@Override
	public <T> Optional<Converter<T>> getConverter(Class<T> forType) {
		Converter converter = doGetConverter(forType);
		return converter == null ? Optional.empty() : Optional.of(converter);
	}

	protected <T> Converter<T> doGetConverter(Class<T> forType) {
		List<Converter> converters = this.converters.getConverters(forType);
		return converters.isEmpty() ? null : converters.get(0);
	}

	@Override
	public <T> T unwrap(Class<T> type) {
		return null;
	}
}
